docs(adr): ADR-0135 mirrors the open half of cloud ADR-0024 — identity & access architecture - #16478
Merged
Merged
Conversation
…y & access architecture Records, in this repository, the mechanism half of `objectstack-ai/cloud` ADR-0024 (Accepted there 2026-06-25) whose code lives here: per-environment identity (D1), the two user populations (D2), the managed-vs-env-native source-of-truth marking (D4), identity-from-the-IdP with authorization decided in the environment (D5) and the SSO-era local user-management surface (D5.2), per-environment SSO with opt-in domain verification (D6), preserved portability and self-host (D7), and env-local users with better-auth-backed organization membership (D9). Every clause carries a symbol anchor into this tree. The commercial half — D3, D5.1, D8, D10 and the V1 roadmap — stays in cloud and is still cited as `cloud ADR-0024`; a table in the record says so, because the re-pointing of today's bare citations is per-site and semantic, not a search-and-replace. No decision is added or widened. Files are not moved and numbers are not reassigned, per the maintainer ruling of 2026-09-02. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
…aimed by an open PR) `0132` is free on `main` but claimed on the branch of an open PR — #16215 adds `docs/adr/0132-multi-organization-runtime-is-open-core.md`. Verified here by a full `git diff --name-only origin/main...<pr-head> -- docs/adr/` over all 27 open PRs, not by titles: 0132 is #16215's, 0133 is #16267's, 0134 is #16476's, and 0135 is unclaimed. File renamed with `git mv`, slug unchanged. The record's one self-citation (its title line) moves with it; the `ADR-0105` and `ADR-0108` references are landed records and are untouched. No line-number anchors were introduced — the file still carries none. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
Collaborator
Author
PM 独立复核 —— PASS。改号已落实,三个门我自己重跑过复核在一个独立 worktree 里做,checkout 到 PR head 门禁,PM 自跑
交付面
⭐ 决策覆盖面,逐条对过
而 D3 · D5.1 · D8 · D10 没有作为决策标题出现(只在 席位对我的两处更正,我接受
|
os-zhuang
approved these changes
Sep 7, 2026
os-zhuang
marked this pull request as ready for review
September 7, 2026 06:37
os-zhuang
enabled auto-merge
September 7, 2026 06:37
This was referenced Sep 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #14506
0132is free onmain, but claimed on the branch of an open PR: #16215 addsdocs/adr/0132-multi-organization-runtime-is-open-core.md. The first commit on this branch used 0132 (the number assigned before that scan existed); the secondgit mvs it to 0135 and moves the record's single self-citation with it. History was not rewritten and nothing was force-pushed.Claim map, measured here by a full
git diff --name-only origin/main...<pr-head> -- docs/adr/over all 27 open PRs — a file-list scan, not a title scan:origin/main(1ecee3e53)⛔ GOVERNED SURFACE — draft PR, human merge. The diff is one file under
docs/adr/**. No seat flips this ready, enqueues it, or arms auto-merge (AGENTS.md Prime Directive #14). Verified mechanically rather than recalled, both directions, on the final filename:node scripts/pm/check-governed-merges.mjs --test docs/adr/0135-identity-and-access-architecture.mddocs/adr/** ×1)node scripts/pm/check-governed-merges.mjs --test packages/plugins/plugin-auth/src/auth-manager.tsWhat this writes
docs/adr/0135-identity-and-access-architecture.md— one new file, 272 lines, nothing else touched.It records in this repository, in this repository's own words, the half of
objectstack-ai/cloudADR-0024 (Status: Accepted, founder, 2026-06-25) whose mechanism governs open code here. Under the maintainer's ruling of 2026-09-02 on #14496 (verbatim 「ok」 to option 2): mirror the open half, ⛔ do not move files, ⛔ do not renumber the cloud record. Shape copied fromdocs/adr/0079-record-display-name.md— a## Provenance — read this before citing this filesection first, naming the cloud record and its date, declaring that the cloud record is the original and keeps the commercial half, and declaring that this file was written from the cloud record's decision list plus the implementing code by a seat with no cloud access.The slug is unchanged across the renumber and deliberately reuses the cloud original's slug, on the precedent ADR-0079 states in its own Provenance section ("deliberately reuses the original's slug … so the two are recognisably one record").
The clauses recorded, and the code anchor each one carries
Every anchor below is a symbol anchor (
path#symbol), was opened and read before it was written, and resolves undernode scripts/check-adr-symbol-anchors.mjs— line-number anchors are a hard finding in this corpus with no transition period, so there are none (the gate's own summary line: "0 line anchors survive").auth-plugin.ts#AuthPlugin,auth-manager.ts#AuthManager,objectql-adapter.ts#AUTH_MODEL_TO_PROTOCOLsys-user.object.ts#source(idp_provisioned/env_native),sys-account.object.tsmanagedBy: 'better-auth') and the row (sys_user.source, stamped by two writers because the two creation paths do not share a seam)sys-user.object.ts#managedBy,sys-member.object.ts#managedBy,sys-sso-provider.object.ts#managedBy, plus theaccount.create.afterstamp inauth-manager.tsand the SCIM-safeafterInsertstamp inauth-plugin.tsresolve-authz-context.ts#resolveUserAuthzGrants,#hasPlatformAdminStanding,invitation-role-cap.ts#isOrgAdminGrade,member-role-canonical.ts#registerMemberRoleCanonicalizationsys-user.object.ts#change_my_password,#change_my_email,#set_user_password,set-initial-password.ts#runSetInitialPassword,last-admin-guard.ts#registerLastAdminGuardADR-0024 ②sys-sso-provider.object.ts#SysSsoProvider,#request_domain_verification,#verify_domain,#domain_verified,register-sso-provider.ts,auth-schema-config.tsOS_SSO_ENABLED,OS_SSO_DOMAIN_VERIFICATION)auth-manager.tssys_userandsys_memberare rows in the environment's database, maintained by the auth stack's endpoints and only read by the guardssys-member.object.ts#SysMember,objectql-adapter.ts#AUTH_MODEL_TO_PROTOCOLWhat stays in cloud, still cited as
cloud ADR-0024D3 cloud-as-IdP hub · D5.1 the cloud operator-portal membership gate · D8 billing · D10 prod/dev metering and population lifecycle · the V1 roadmap and commercial framing. The record carries this as a table, with a consequence stated for #14361: a bare
ADR-0024in this tree is not mechanically re-pointable, because some sites mean a clause that stays in cloud —auth-manager.tscitesADR-0024 V1for the SSO default-role provisioning. That re-pointing is #14361's per-site call and is not touched here. The env-side SCIM mechanism iscloud ADR-0071's, mirrored by #16476, and is likewise not restated here.⛔ No decision is added and none is widened. Anything cloud ADR-0024 did not decide is not decided by this file, and the record says so in its own "What this record does NOT settle" section.
⛔ The record cites no number that does not yet exist under
docs/adr/— in particular not the sibling mirrors' 0133 / 0134, whichcheck-adr-anchorswould read as a squat.Measurements I took myself
1.
git -C ../cloud show origin/main:docs/adr/0024-...— SKIPPED, not run. This seat has nocloudcheckout and nocloudaccess. Nothing here is derived from the cloud file; the record's Provenance section states that limitation in the document itself, so a future reader is not misled about what was checked.2. The bare-citation count — measured here, and it does not reproduce the card's figure.
5a9138703(this branch's base)00ff228fe(the commit the card cites)So the card's
# expect ~71is the unfiltered count at00ff228fe— the annotation and the command it is attached to disagree by thegrep -vline. The record quotes 64 (this branch's own base), and labels it as such. Not a blocker for this card; it does matter to #14361, whose scope figure comes from the same reading.3.
docs/adr/PRIORITIZATION.md— NOT registered, deliberately. The card says to register the new file there if that index is maintained by hand, judged from the file's own header. Its own header says it is not an index:It is a dated 2026-06-12 review of "All 49 ADRs in
docs/adr/", not a registry, anddocs/adr/holds no other index file. Adding a 2026-09 record to a snapshot of 2026-06 would falsify it. No ADR since0050is listed there either.Gates — the full table, mechanically derived
Derived, not recalled:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands(17 commands), re-derived after the renumber commit and byte-identical to the earlier derivation. The union below was re-run in full at3d7762e3e, the head of this PR; each exit code was captured immediately after its own redirect, never through a pipe.node scripts/check-adr-links.mjsnode scripts/check-adr-links.mjs --self-testnode scripts/check-adr-symbol-anchors.mjsnode scripts/check-adr-symbol-anchors.mjs --self-testnode scripts/check-ci-filter-parity.mjsnode scripts/check-closing-keyword-parity.mjsnode scripts/check-closing-keyword-parity.mjs --self-testnode scripts/check-comment-mask-corpus.mjspnpm --filter @objectstack/lint run check:doc-formula-expressionspnpm check:adr-anchorspnpm check:cross-package-test-inputspnpm check:doc-authoringpnpm check:driver-memory-censuspnpm check:nul-bytespnpm check:pm-governed-mergespnpm check:refd-timer-probepnpm check:watch-hint-literalcheck-doc-formula-expressionsrefuses withPREREQUISITE NOT MET — the workspace package @objectstack/formula is not builtand measures nothing. Cleared both times by building the two declared prerequisites (turbo run build --filter=@objectstack/formula --filter=@objectstack/lint, run underscripts/pm/os-verify-lock.sh) and re-running, which is the 0 in the table.Three further families the derivation names as NOT MEASURED here, quoted rather than silently omitted:
check-cross-package-test-inputs --union-into …,check-shard-attestation --emit …andcheck-test-completeness …each take a value from the workflow that has none outside a CI run. The derivation also reports 10 families whose declared population is too wide to place and 41 artifact-roster families scoredsilentfor every card in the tree — ⛔ neither group is a clearance, and none of their rosters sits in a directory this PR's single path is in.Changeset
None, and
skip-changesetapplied — this diff publishes nothing from any package. Not from memory: the three most recent ADR-only landings in this repo did exactly this — #14976 (ADR-0131), #14151 (ADR-0130) and #12519 (ADR-0127) each landed with no.changeset/file and theskip-changesetlabel, read back off the PRs themselves. The one recent counter-example proves the rule: #13067 (ADR-0129) carried a changeset because it also changedpackages/specandplugin-authcode.Not in scope, filed separately
A stale filename found while verifying anchors:
last-admin-ban-guard.tshas not existed since the guard was renamed tolast-admin-guard.ts, yet five comments still point at it (invitation-role-cap.ts,objectql-adapter.ts,break-glass-local-credential.test.ts×2, andobjectql-adapter.test.tspointing at alast-admin-ban-guard.test.tsthat does not exist). Filed as #16477 (labelfinding, unassigned); ⛔ not fixed here — this PR touchesdocs/adr/**only. #16477 remains open.